昨天我們成功將 ImageBitmap
送到 worker
,今天我們要來解決的是如何在 worker
裡面處理 Canvas
的更新,因為在 worker
是沒辦法獲取 DOM
。
OffscreenCanvas 提供了一個 Cavnas
,讓使用上並不一定要在可以獲得 DOM
的環境,也就是說在 worker
中也可以使用。所以我們只要將 OffscreenCanvas
傳遞給 worker
,就可以在裡面直接完成更新,不需要一定要在主線程裡面更新。那來看一下用法吧。
const offscreen = canvas.transferControlToOffscreen()
worker.postMessage({ offscreen, type: 'init', width: this.width,
height: this.height }, [offscreen])
其實很簡單,直接對我們原本的 Canvas
,使用 transferControlToOffscreen
轉換類型,之後直接傳給 worker
使用即可,而因為 OffscreenCanvas
有實作 Transferable
介面,所以記得使用第二個參數,詳情可以看昨天。這樣我們有可以在 worker
中使用的 canvas
了。
但要注意之前提到的,轉移到
worker
的物件是不能繼續在主線程使用的,所以如果在後面不小心使用到的話會出錯唷!
繼續看 worker
,這邊就不對程式碼做優化了,當 onmessage
需要監聽的事件多時記得要處理
let canvas
let context
let imageBitmapTmp
let width
let height
let sliderValueTmp
let play
let barrage
const draw = () => {
context.drawImage(imageBitmapTmp, 0, 0, width, height)
const pixelData = context.getImageData(0, 0, width, height)
const result = applyFilters(pixelData, sliderValueTmp)
context.putImageData(result, 0, 0)
barrage.draw()
requestAnimationFrame(draw)
}
onmessage = function(e) {
if (e.data.type === 'init') {
canvas = e.data.offscreen
context = canvas.getContext('2d')
width = e.data.width
height = e.data.height
barrage = new Barrage({ ctx: context, width, height })
} else if (e.data.type === 'process' && context) {
const { imageBitmap, sliderValue } = e.data
imageBitmapTmp = imageBitmap
sliderValueTmp = sliderValue
if (!play) {
play = true
draw()
}
}
一樣在 onmessage
裡面新增監聽的處理,把一些初始值處理好,接著再開始動作的時候觸發 draw
,其實會發現跟原本的流程及程式碼幾乎都一模一樣,只是差別在我們是在 worker
裡面執行,所以原本在主線程裡面會導致卡頓的計算,因為我們直接在 worker
裡面計算及更新到 canvas
的畫面上,會發現畫面不會在卡頓,看一下效果吧!
可以看到在主線程繁忙的時候,拖拉畫面是會卡頓的,而當使用 OffscreenCanvas
之後,畫面雖然還是會卡頓,但是在主線程的拖拉上是不會被影響的。
因為我們在主線程裡面還是需要從
video
裡面獲取影像再傳給worker
,所以這時候如果主線程有一些耗時計算的話,還是會影響影片的更新,但已經把已知最耗時的計算操作及更新留在worker
裡面去做了,但如果使用方式是Canvas
自己在worker
裡面就可以完成的動畫或計算的話,不管主線程是有多少計算,兩邊因為沒溝通的關係是會互不影響的。
接著就是最關心的效率有沒有提升了,在這邊我們都用 fps 做為比較,然後使用三個濾鏡飽和度、暗部,銳利化來做測試,用 100 個影格來作平均,主線程 大約等於 4.9 fps,而使用 OffscreenCanvas
大約等於 5.8 fps,大約增加了快 20% 的效率。而且也不會讓主線程卡頓,讚讚讚。可惜的是現在支援度真的不佳,有看到其他人用在 Three
上面,如果有大量使用 Canvas
的應用記得確認使用者瀏覽器有沒有支援唷!
到這邊,我們調整效能的第一步已經完成了,介紹了兩個很新的 OffscreenCanvas
以及 ImageBitmap
,將計算移動到其他線程,也附帶增加了一些效率與使用者體驗。
接下來開始就到我們的第二步,減少計算所需要的時間囉!明天見~